#include "Rp2OpenGLRenderer.h"
#include "Rp2OpenGLResources.h"
#include "gl/GLU.h"

using namespace Rp2;


GLenum OpenGLRenderer::ms_aeTextureMipmap[Texture::MAX_FILTER_TYPES] =
{
    GL_NEAREST,
    GL_LINEAR,
    GL_NEAREST_MIPMAP_NEAREST,
    GL_NEAREST_MIPMAP_LINEAR,
    GL_LINEAR_MIPMAP_NEAREST,
    GL_LINEAR_MIPMAP_LINEAR
};

GLenum OpenGLRenderer::ms_aeWrapMode[Texture::MAX_WRAP_TYPES] =
{
    GL_CLAMP,
    GL_REPEAT,
    GL_MIRRORED_REPEAT,
    GL_CLAMP_TO_BORDER,
    GL_CLAMP_TO_EDGE
};

GLenum OpenGLRenderer::ms_aeImageComponents[Image::IT_QUANTITY] =
{
    GL_RGB8,               // Image::IT_RGB888
    GL_RGBA8,              // Image::IT_RGBA8888
    GL_DEPTH_COMPONENT16,  // Image::IT_DEPTH16
    GL_DEPTH_COMPONENT24,  // Image::IT_DEPTH24
    GL_DEPTH_COMPONENT32,  // Image::IT_DEPTH32
    GL_RGB8,               // Image::IT_CUBE_RGB888
    GL_RGBA8,              // Image::IT_CUBE_RGBA8888
    GL_RGB32F_ARB,         // Image::IT_RGB32F
    GL_RGBA32F_ARB,        // Image::IT_RGBA32F
    GL_RGB16F_ARB,         // Image::IT_RGB16F
    GL_RGBA16F_ARB,        // Image::IT_RGBA16F
    GL_RGB16,              // Image::IT_RGB16I
    GL_RGBA16,             // Image::IT_RGBA16I
    GL_INTENSITY8,         // Image::IT_INTENSITY8I
    GL_INTENSITY16,        // Image::IT_INTENSITY16I
    GL_INTENSITY16F_ARB,   // Image::IT_INTENSITY16F
    GL_INTENSITY32F_ARB,   // Image::IT_INTENSITY32F
    GL_RGB5,               // Image::IT_RGB565
    GL_RGB5_A1,            // Image::IT_RGBA5551
    GL_RGBA4,              // Image::IT_RGBA4444
    GL_RGB8,               // Image::IT_BGR888
    GL_RGBA8,              // Image::IT_BGRA8888
};

GLenum OpenGLRenderer::ms_aeImageFormats[Image::IT_QUANTITY] =
{
    GL_RGB,              // Image::IT_RGB888
    GL_RGBA,             // Image::IT_RGBA8888
    GL_DEPTH_COMPONENT,  // Image::IT_DEPTH16
    GL_DEPTH_COMPONENT,  // Image::IT_DEPTH24
    GL_DEPTH_COMPONENT,  // Image::IT_DEPTH32
    GL_RGB,              // Image::IT_CUBE_RGB888
    GL_RGBA,             // Image::IT_CUBE_RGBA8888
    GL_RGB,              // Image::IT_RGB32F
    GL_RGBA,             // Image::IT_RGBA32F
    GL_RGB,              // Image::IT_RGB16F
    GL_RGBA,             // Image::IT_RGBA16F
    GL_RGB,              // Image::IT_RGB16I
    GL_RGBA,             // Image::IT_RGBA16I
    GL_INTENSITY,        // Image::IT_INTENSITY8I,
    GL_INTENSITY,        // Image::IT_INTENSITY16I,
    GL_INTENSITY,        // Image::IT_INTENSITY16F,
    GL_INTENSITY,        // Image::IT_INTENSITY32F
    GL_RGB,              // Image::IT_RGB565
    GL_RGBA,             // Image::IT_RGBA5551
    GL_RGBA,             // Image::IT_RGBA4444
	GL_BGR,				 // Image::IT_BGR888
	GL_BGRA				 // Image::IT_BGRA888
};

GLenum OpenGLRenderer::ms_aeImageTypes[Image::IT_QUANTITY] =
{
    GL_UNSIGNED_BYTE,               // Image::IT_RGB888
    GL_UNSIGNED_BYTE,               // Image::IT_RGBA8888
    GL_FLOAT,                       // Image::IT_DEPTH16
    GL_FLOAT,                       // Image::IT_DEPTH24
    GL_DEPTH_COMPONENT,             // Image::IT_DEPTH32
    GL_UNSIGNED_BYTE,               // Image::IT_CUBE_RGB888
    GL_UNSIGNED_BYTE,               // Image::IT_CUBE_RGBA8888
    GL_FLOAT,                       // Image::IT_RGB32F
    GL_FLOAT,                       // Image::IT_RGBA32F
    GL_HALF_FLOAT_ARB,              // Image::IT_RGB16F
    GL_HALF_FLOAT_ARB,              // Image::IT_RGBA16F
    GL_UNSIGNED_SHORT,              // Image::IT_RGB16I
    GL_UNSIGNED_SHORT,              // Image::IT_RGBA16I
    GL_UNSIGNED_BYTE,               // Image::IT_INTENSITY8I
    GL_UNSIGNED_SHORT,              // Image::IT_INTENSITY16I
    GL_HALF_FLOAT_ARB,              // Image::IT_INTENSITY16F
    GL_FLOAT,                       // Image::IT_INTENSITY32F
    GL_UNSIGNED_SHORT_5_6_5_REV,    // Image::IT_RGB565
    GL_UNSIGNED_SHORT_1_5_5_5_REV,  // Image::IT_RGBA5551
    GL_UNSIGNED_SHORT_4_4_4_4_REV,  // Image::IT_RGBA4444
    GL_UNSIGNED_BYTE,               // Image::IT_BGR888
    GL_UNSIGNED_BYTE,               // Image::IT_BGRA8888
};

GLenum OpenGLRenderer::ms_aeSamplerTypes[
    SamplerInformation::MAX_SAMPLER_TYPES] =
{
    GL_TEXTURE_1D,        // SamplerInformation::SAMPLER_1D
    GL_TEXTURE_2D,        // SamplerInformation::SAMPLER_2D
    GL_TEXTURE_3D,        // SamplerInformation::SAMPLER_3D
    GL_TEXTURE_CUBE_MAP,  // SamplerInformation::SAMPLER_CUBE
    GL_TEXTURE_2D,        // SamplerInformation::SAMPLER_PROJ
};

GLenum OpenGLRenderer::ms_aeMapTypes[3] =
{
    GL_READ_ONLY,   // Renderer::DT_READ
    GL_WRITE_ONLY,  // Renderer::DT_WRITE
    GL_READ_WRITE   // Renderer::DT_READ_WRITE
};

GLenum OpenGLRenderer::ms_aeObjectType[Geometry::GT_MAX_QUANTITY] =
{
    GL_POINTS,          // GT_POLYPOINT
    GL_LINES,           // GT_POLYLINE_SEGMENTS
    GL_LINE_STRIP,      // GT_POLYLINE_OPEN
    GL_LINE_STRIP,      // GT_POLYLINE_CLOSED (Wm4::Polyline has extra index)
    GL_TRIANGLES,       // GT_TRIMESH
    GL_TRIANGLE_STRIP,  // GT_TRISTRIP
    GL_TRIANGLE_FAN     // GT_TRIFAN
};

//---------------------------------------------------------------------------------------------------
OpenGLRenderer::OpenGLRenderer( int iWinWidth, int iWinHeight)
	:
	Renderer(iWinWidth, iWinHeight)
{

}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::ClearBuffers()
{
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);	
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::InitializeState()
{
	SetGlobalState(GlobalState::DefaultStates);
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::DrawElements()
{
    VertexBuffer* pkVBuffer = m_pkGeometry->VBuffer;
    IndexBuffer* pkIBuffer = m_pkGeometry->IBuffer;
    assert(pkIBuffer);
    GLenum eType = ms_aeObjectType[m_pkGeometry->Type];
    int iOffset = pkIBuffer->GetOffset();	

	// for debug
// 	System::MsgBoxPrintf("","Vertex quantity : %d, Index Quantity : %d",
// 		pkVBuffer->GetVQuantity(), pkIBuffer->GetIndexQuantity());

    glDrawRangeElements(eType,0,pkVBuffer->GetVQuantity()-1,
        pkIBuffer->GetIndexQuantity(),GL_UNSIGNED_INT,
        (const GLvoid*)((int*)0 + iOffset));
/*	glDrawElements(eType, pkIBuffer->GetIndexQuantity()*3, GL_UNSIGNED_INT, 0);*/
}	
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnLoadVProgram(ResourceIdentifier*& rpkID, VertexProgram* pkVProgram)
{
    // Generate binding information and compile the program.
    VProgramID* pkResource = new VProgramID;
    rpkID = pkResource;

    const char* acProgramText = pkVProgram->GetProgramText().c_str();
    int iProgramLength = (int)strlen(acProgramText);

    glEnable(GL_VERTEX_PROGRAM_ARB);
    glGenProgramsARB(1,&pkResource->ID);
    glBindProgramARB(GL_VERTEX_PROGRAM_ARB,pkResource->ID);
    glProgramStringARB(GL_VERTEX_PROGRAM_ARB,GL_PROGRAM_FORMAT_ASCII_ARB,
        iProgramLength,acProgramText);
    glDisable(GL_VERTEX_PROGRAM_ARB);	
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnReleaseVProgram(ResourceIdentifier* pkID)
{
    VProgramID* pkResource = (VProgramID*)pkID;
    glDeleteProgramsARB(1,&pkResource->ID);
    delete pkResource;
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnLoadPProgram(ResourceIdentifier*& rpkID, PixelProgram* pkPProgram)
{
    // Generate binding information and compile the shader.
    PProgramID* pkResource = new PProgramID;
    rpkID = pkResource;

    const char* acProgramText = pkPProgram->GetProgramText().c_str();
    int iProgramLength = (int)strlen(acProgramText);

    glEnable(GL_FRAGMENT_PROGRAM_ARB);
    glGenProgramsARB(1,&pkResource->ID);
    glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB,pkResource->ID);
    glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,GL_PROGRAM_FORMAT_ASCII_ARB,
        iProgramLength,acProgramText);
    glDisable(GL_FRAGMENT_PROGRAM_ARB);	
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnReleasePProgram(ResourceIdentifier* pkID)
{
	PProgramID* pkResource = (PProgramID*)pkID;
    glDeleteProgramsARB(1,&pkResource->ID);
	delete pkResource;
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnLoadTexture(ResourceIdentifier*& rpkID, Texture* pkTexture)
{
    // Activate the texture unit in hardware that will manage this texture.
    TextureID* pkResource = new TextureID;
    pkResource->TextureObject = pkTexture;
    rpkID = pkResource;

    // Get the texture image and its information.
    const Image* pkImage = pkTexture->GetImage();
    assert(pkImage);
    int iDimension = pkImage->GetDimension();
    unsigned char* aucData = pkImage->GetData();
    int iComponent = ms_aeImageComponents[pkImage->GetFormat()];
    int eFormat = ms_aeImageFormats[pkImage->GetFormat()];
    int eIType = ms_aeImageTypes[pkImage->GetFormat()];

    bool bIsRegularImage = !pkImage->IsCubeImage();
    int eTarget;
    if (bIsRegularImage)
    {
        eTarget = ms_aeSamplerTypes[iDimension-1];
    }
    else
    {
        eTarget = GL_TEXTURE_CUBE_MAP;
    }

    // Generate the name and binding information.
    glGenTextures((GLsizei)1,&pkResource->ID);
    glBindTexture(eTarget,pkResource->ID);

//    if(DynamicCast<DynamicTexture>(pkTexture))
//        {
//             // Bind a pixel buffer object as the data source of the texture.
//            glGenBuffers(1,&pkResource->PBOHandle);
//            glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB,pkResource->PBOHandle);
//     
//           GLsizeiptr iNumBytes =
//                (GLsizeiptr)(pkImage->GetBytesPerPixel()*pkImage->GetQuantity());
//     
//            glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB,iNumBytes,aucData,
//                GL_DYNAMIC_DRAW);
//     
//             aucData = 0;
//         }
//         else
//         {
        pkResource->PBOHandle = 0;
//    }

    // Enable anisotropic filtering (if requested).
    float fAnisotropy = pkTexture->GetAnisotropyValue();
    if (1.0f < fAnisotropy && fAnisotropy <= ms_fMaxAnisotropy)
    {
        glTexParameterf(eTarget,GL_TEXTURE_MAX_ANISOTROPY_EXT,fAnisotropy);
    }
    else
    {
        glTexParameterf(eTarget,GL_TEXTURE_MAX_ANISOTROPY_EXT,1.0f);
    }

    // Set the filter mode.
    Texture::FilterType eFType = pkTexture->GetFilterType();
    if (eFType == Texture::NEAREST
    ||  iComponent == GL_RGB32F_ARB
    ||  iComponent == GL_RGBA32F_ARB)
    {
        glTexParameteri(eTarget,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
    }
    else
    {
        glTexParameteri(eTarget,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    }

    // Set the mipmap mode.
    if(iComponent == GL_RGB32F_ARB || iComponent == GL_RGBA32F_ARB)
    {
        glTexParameteri(eTarget,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    }
    else
    {
        glTexParameteri(eTarget,GL_TEXTURE_MIN_FILTER,
            ms_aeTextureMipmap[eFType]);
    }

    // Set the border color (for clamp to border).
    glTexParameterfv(eTarget,GL_TEXTURE_BORDER_COLOR,
        (const float*)pkTexture->GetBorderColor());

    // Copy the image data from system memory to video memory.
    bool bNoMip =
        (eFType == Texture::NEAREST || eFType == Texture::LINEAR);

    switch (iDimension)
    {
    case 1:
        if (bNoMip)
        {
            glTexImage1D(eTarget,0,iComponent,pkImage->GetBound(0),0,
                eFormat,eIType,aucData);
        }
        else
        {
//             gluBuild1DMipmaps(eTarget,iComponent,pkImage->GetBound(0),
//                 eFormat,eIType,aucData);
            glTexImage1D(eTarget,0,iComponent,pkImage->GetBound(0),0,
                eFormat,eIType,aucData);
			glGenerateMipmap(GL_TEXTURE_1D);
		}
        glTexParameteri(eTarget,GL_TEXTURE_WRAP_S,
            ms_aeWrapMode[pkTexture->GetWrapType(0)]);
        break;

    case 2:
        if (bIsRegularImage)
        {
            if (bNoMip)
            {
                glTexImage2D(eTarget,0,iComponent,pkImage->GetBound(0),
                    pkImage->GetBound(1),0,eFormat,eIType,aucData);
            }
            else
            {
//                 gluBuild2DMipmaps(eTarget,iComponent,pkImage->GetBound(0),
//                     pkImage->GetBound(1),eFormat,eIType,aucData);
                glTexImage2D(eTarget,0,iComponent,pkImage->GetBound(0),
                    pkImage->GetBound(1),0,eFormat,eIType,aucData);
				glGenerateMipmap(GL_TEXTURE_2D);            
			}
        }
        else
        {
            // A cube map image has 6 subimages (+x,-x,+y,-y,+z,-z).
            int iDelta = pkImage->GetBytesPerPixel()*pkImage->GetQuantity();
            int i;

            if (bNoMip)
            {
                for (i = 0; i < 6; i++, aucData += iDelta)
                {
                    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,0,
                        iComponent,pkImage->GetBound(0),pkImage->GetBound(1),
                        0,eFormat,eIType,aucData);
                }
            }
            else
            {
                for (i = 0; i < 6; i++, aucData += iDelta)
                {
                    //gluBuild2DMipmaps(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,
                    //    iComponent,pkImage->GetBound(0),pkImage->GetBound(1),
                    //    eFormat,eIType,aucData);
                    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i,0,
                        iComponent,pkImage->GetBound(0),pkImage->GetBound(1),
                        0,eFormat,eIType,aucData);
					glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
                }
            }
        }

        glTexParameteri(eTarget,GL_TEXTURE_WRAP_S,
            ms_aeWrapMode[pkTexture->GetWrapType(0)]);
        glTexParameteri(eTarget,GL_TEXTURE_WRAP_T,
            ms_aeWrapMode[pkTexture->GetWrapType(1)]);
        break;

    case 3:
        // TO DO.  Microsoft's GLU library does not implement the function
        // gluBuild3DMipmaps.  DirectX9 SDK does not support automatic
        // generation of mipmaps for volume textures.  For now, do not
        // support mipmaps of 3D textures.  However, manually generated
        // mipmaps can be added later.  The LibGraphics Sampler classes
        // already implement this for software rendering.
        glTexImage3D(eTarget,0,iComponent,pkImage->GetBound(0),
            pkImage->GetBound(1),pkImage->GetBound(2),0,eFormat,eIType,
            aucData);

        glTexParameteri(eTarget,GL_TEXTURE_WRAP_S,
            ms_aeWrapMode[pkTexture->GetWrapType(0)]);
        glTexParameteri(eTarget,GL_TEXTURE_WRAP_T,
            ms_aeWrapMode[pkTexture->GetWrapType(1)]);
        glTexParameteri(eTarget,GL_TEXTURE_WRAP_R,
            ms_aeWrapMode[pkTexture->GetWrapType(2)]);
        break;

    default:
        assert(false);
        break;
    }

    if (pkResource->PBOHandle > 0)
    {
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB,0);
    }
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnReleaseTexture (ResourceIdentifier* pkID)
{
    TextureID* pkResource = (TextureID*)pkID;
    if (pkResource->PBOHandle > 0)
    {
        glDeleteBuffers(1,&pkResource->PBOHandle);
    }
    glDeleteTextures(1,&pkResource->ID);
    delete pkResource;
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnLoadVBuffer (ResourceIdentifier*& rpkID,
    const Attributes& rkIAttr, const Attributes& rkOAttr,
    VertexBuffer* pkVBuffer)
{
    VBufferID* pkResource = new VBufferID;
    rpkID = pkResource;
    pkResource->IAttr = rkIAttr;
    pkResource->OAttr = rkOAttr;

    int iChannels;
    float* afCompatible = 0;  // allocated by CompactData
    pkVBuffer->CompactData(rkIAttr,false,iChannels,afCompatible);

    // Generate the name and binding information.
    glGenBuffers(1,&pkResource->ID);
    glBindBuffer(GL_ARRAY_BUFFER,pkResource->ID);

    // Copy the vertex buffer data from system memory to video memory.
    glBufferData(GL_ARRAY_BUFFER,iChannels*sizeof(float),afCompatible,
        GL_STATIC_DRAW);

    delete[] afCompatible;
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnReleaseVBuffer (ResourceIdentifier* pkID)
{
    VBufferID* pkResource = (VBufferID*)pkID;
    glDeleteBuffers(1,&pkResource->ID);
    delete pkResource;
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnLoadIBuffer (ResourceIdentifier*& rpkID,
    IndexBuffer* pkIBuffer)
{
    IBufferID* pkResource = new IBufferID;
    rpkID = pkResource;

    glGenBuffers(1,&pkResource->ID);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,pkResource->ID);

    // Copy the index buffer data from system memory to video memory.
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
        pkIBuffer->GetIndexQuantity()*sizeof(int),pkIBuffer->GetData(),
        GL_STATIC_DRAW);
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnReleaseIBuffer (ResourceIdentifier* pkID)
{
    IBufferID* pkResource = (IBufferID*)pkID;
    glDeleteBuffers(1,&pkResource->ID);
    delete pkResource;
}
//---------------------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------------------
// Resource enable and disable
//---------------------------------------------------------------------------------------------------
// Some OpenGL 2.x drivers are not handling normal attributes correctly.  This
// is a problem should you want to use the normal vector to pass a 4-tuple of
// information to the shader.  The OpenGL 1.x glNormalPointer assumes the
// normals are 3-tuples.  If you know that your target machines correctly
// support OpenGL 2.x normal attributes, expose the following #define.
// Otherwise, the renderer will use the OpenGL 1.x glNormalPointer.
//
/*#define USE_OPENGL2_NORMAL_ATTRIBUTES*/

void OpenGLRenderer::OnEnableVBuffer (ResourceIdentifier* pkID)
{
    // Bind the current vertex buffer.
    VBufferID* pkResource = (VBufferID*)pkID;
    glBindBuffer(GL_ARRAY_BUFFER,pkResource->ID);

    const Attributes& rkRAttr = pkResource->IAttr;
    GLsizei iSize = (GLsizei)(sizeof(float)*rkRAttr.GetChannelQuantity());
    const float* afData = 0;

    if (rkRAttr.HasPosition())
    {
        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(rkRAttr.GetPositionChannels(),GL_FLOAT,iSize,
            afData + rkRAttr.GetPositionOffset());
    }

    if (rkRAttr.HasBlendWeight())
    {
//         glEnableClientState(GL_WEIGHT_ARRAY_ARB);
//         glWeightPointerARB(rkRAttr.GetBlendWeightChannels(),GL_FLOAT,iSize,
//             (GLvoid*)(afData + rkRAttr.GetBlendWeightOffset()));

        glEnableVertexAttribArrayARB(1);        
		glVertexAttribPointerARB(1,rkRAttr.GetBlendWeightChannels(),GL_FLOAT,
            GL_FALSE,iSize,afData + rkRAttr.GetBlendWeightOffset());		
    }

    if (rkRAttr.HasNormal())
    {
#ifdef USE_OPENGL2_NORMAL_ATTRIBUTES
        glEnableVertexAttribArrayARB(2);
        glVertexAttribPointerARB(2,rkRAttr.GetNormalChannels(),GL_FLOAT,
            GL_FALSE,iSize,afData + rkRAttr.GetNormalOffset());
#else
        glEnableClientState(GL_NORMAL_ARRAY);
        glNormalPointer(GL_FLOAT,iSize,afData + rkRAttr.GetNormalOffset());
#endif
    }

    if (rkRAttr.HasColor(0))
    {
        glEnableClientState(GL_COLOR_ARRAY);
        glColorPointer(rkRAttr.GetColorChannels(0),GL_FLOAT,iSize,
            afData + rkRAttr.GetColorOffset(0));
    }

    if (rkRAttr.HasColor(1))
    {
        glEnableClientState(GL_SECONDARY_COLOR_ARRAY);
        glSecondaryColorPointer(rkRAttr.GetColorChannels(1),GL_FLOAT,iSize,
            afData + rkRAttr.GetColorOffset(1));
    }

    if (rkRAttr.HasFog())
    {
        glEnableClientState(GL_FOG_COORDINATE_ARRAY_EXT);
        glFogCoordPointerEXT(GL_FLOAT,iSize,afData + rkRAttr.GetFogOffset());
    }

    if (rkRAttr.HasPSize())
    {
        glEnableVertexAttribArrayARB(6);
        glVertexAttribPointerARB(6,rkRAttr.GetPSizeChannels(),GL_FLOAT,
            GL_FALSE,iSize,afData + rkRAttr.GetPSizeOffset());
    }

    if (rkRAttr.HasBlendIndices())
    {
		
        //glEnableClientState(GL_MATRIX_INDEX_ARRAY_ARB);
        //glMatrixIndexPointerARB(rkRAttr.GetBlendIndicesChannels(), GL_FLOAT,
        //    iSize,(GLvoid*)(afData + rkRAttr.GetBlendIndicesOffset()));
        glEnableVertexAttribArrayARB(7);
        glVertexAttribPointerARB(7,rkRAttr.GetBlendIndicesChannels(),GL_FLOAT,
            GL_FALSE,iSize,afData + rkRAttr.GetBlendIndicesOffset());
    }

    for (int iUnit = 0; iUnit < rkRAttr.GetMaxTCoords(); iUnit++)
    {
        if (rkRAttr.HasTCoord(iUnit))
        {
            glClientActiveTexture(GL_TEXTURE0 + iUnit);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY); 
            glTexCoordPointer(rkRAttr.GetTCoordChannels(iUnit),GL_FLOAT,iSize,
                afData + rkRAttr.GetTCoordOffset(iUnit));
        }
    }

    if (rkRAttr.HasTangent())
    {
        glEnableVertexAttribArrayARB(14);
        glVertexAttribPointerARB(14,rkRAttr.GetTangentChannels(),GL_FLOAT,
            GL_FALSE,iSize,afData + rkRAttr.GetTangentOffset());
    }

    if (rkRAttr.HasBitangent())
    {
        glEnableVertexAttribArrayARB(15);
        glVertexAttribPointerARB(15,rkRAttr.GetBitangentChannels(),GL_FLOAT,
            GL_FALSE,iSize,afData + rkRAttr.GetBitangentOffset());
    }
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnDisableVBuffer (ResourceIdentifier* pkID)
{
    VBufferID* pkResource = (VBufferID*)pkID;
    const Attributes& rkRAttr = pkResource->IAttr;

    // Unbind the current vertex buffer.
    glBindBuffer(GL_ARRAY_BUFFER,0);

    if (rkRAttr.HasPosition())
    {
        glDisableClientState(GL_VERTEX_ARRAY);
    }

    if (rkRAttr.HasBlendWeight())
    {
        glDisableClientState(GL_WEIGHT_ARRAY_ARB);
    }

    if (rkRAttr.HasNormal())
    {
#ifdef USE_OPENGL2_NORMAL_ATTRIBUTES
        glDisableVertexAttribArrayARB(2);
#else
        glDisableClientState(GL_NORMAL_ARRAY);
#endif
    }

    if (rkRAttr.HasColor(0))
    {
        glDisableClientState(GL_COLOR_ARRAY);
    }

    if (rkRAttr.HasColor(1))
    {
        glDisableClientState(GL_SECONDARY_COLOR_ARRAY);
    }

    if (rkRAttr.HasFog())
    {
        glDisableClientState(GL_FOG_COORDINATE_ARRAY_EXT);
    }

    if (rkRAttr.HasPSize())
    {
        glDisableVertexAttribArrayARB(6);
    }

    if (rkRAttr.HasBlendIndices())
    {
        glDisableClientState(GL_MATRIX_INDEX_ARRAY_ARB);
    }

    for (int iUnit = 0; iUnit < rkRAttr.GetMaxTCoords(); iUnit++)
    {
        if (rkRAttr.HasTCoord(iUnit))
        {
            glClientActiveTexture(GL_TEXTURE0 + iUnit);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        }
    }

    if (rkRAttr.HasTangent())
    {
        glDisableVertexAttribArrayARB(14);
    }

    if (rkRAttr.HasBitangent())
    {
        glDisableVertexAttribArrayARB(15);
    }
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnEnableIBuffer (ResourceIdentifier* pkID)
{
    // Bind the current index buffer.
    IBufferID* pkResource = (IBufferID*)pkID;
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,pkResource->ID);
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnDisableIBuffer (ResourceIdentifier*)
{
    // Unbind the current index buffer.
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::SetVProgramConstant (int eCType, int iBaseRegister,
    int iRegisterQuantity, float* afData)
{
    if (eCType != Renderer::CT_NUMERICAL)
    {
        for (int j = 0; j < iRegisterQuantity; j++)
        {
            glProgramLocalParameter4fvARB(GL_VERTEX_PROGRAM_ARB,
                (GLuint)iBaseRegister,afData);
            iBaseRegister++;
            afData += 4;
        }
    }
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::SetPProgramConstant (int eCType, int iBaseRegister,
    int iRegisterQuantity, float* afData)
{
    if (eCType != Renderer::CT_NUMERICAL)
    {
        for (int j = 0; j < iRegisterQuantity; j++)
        {
            glProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB,
                (GLuint)iBaseRegister,afData);
            iBaseRegister++;
            afData += 4;
        }
    }
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnEnableVProgram (ResourceIdentifier* pkID)
{
    VProgramID* pkResource = (VProgramID*)pkID;
    glEnable(GL_VERTEX_PROGRAM_ARB);
    glBindProgramARB(GL_VERTEX_PROGRAM_ARB,pkResource->ID);
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnDisableVProgram (ResourceIdentifier*)
{
    glDisable(GL_VERTEX_PROGRAM_ARB);
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnEnablePProgram (ResourceIdentifier* pkID)
{
    PProgramID* pkResource = (PProgramID*)pkID;
    glEnable(GL_FRAGMENT_PROGRAM_ARB);
    glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB,pkResource->ID);
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnDisablePProgram (ResourceIdentifier*)
{
    glDisable(GL_FRAGMENT_PROGRAM_ARB);
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnEnableTexture (ResourceIdentifier* pkID)
{
    SamplerInformation* pkSI = m_apkActiveSamplers[m_iCurrentSampler];
    SamplerInformation::Type eSType = pkSI->GetType();
    int iTextureUnit = pkSI->GetTextureUnit();
    int eTarget = ms_aeSamplerTypes[eSType];

    TextureID* pkResource = (TextureID*)pkID;
    glActiveTexture(GL_TEXTURE0 + iTextureUnit);
    glBindTexture(eTarget,pkResource->ID);
    if (pkResource->PBOHandle > 0)
    {
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB,pkResource->PBOHandle);
    }
}
//---------------------------------------------------------------------------------------------------
void OpenGLRenderer::OnDisableTexture (ResourceIdentifier* pkID)
{
    TextureID* pkResource = (TextureID*)pkID;
    if (pkResource->PBOHandle > 0)
    {
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB,0);
    }
}
//---------------------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------